#define MULTI_THREAD
#include <stdio.h>
#include <windows.h> /* For the CreateThread prototype */
#include <process.h>
#include <GL/glut.h>
#include <time.h>

void Render1(PVOID param); /* Function prototype */
void Render2(PVOID param);
void Render3(PVOID param);

int nargs, totalcubes;
char **args;

static GLfloat spin[1000] = {0.0};

/* Define color vectors */
GLfloat RED[3] = {1.0f, 0.0f, 0.0f};
GLfloat GREEN[3] = {0.0f, 1.0f, 0.0f};
GLfloat BLUE[3] = {0.0f, 0.0f, 1.0f};
GLfloat ORANGE[3] = {1.0f, 0.5f, 0.0f};
GLfloat YELLOW[3] = {1.0f, 1.0f, 0.0f};
GLfloat VIOLET[3] = {1.0f, 0.0f, 1.0f};
GLfloat CYAN[3] = {0.0f, 1.0f, 1.0f};

void init(void) {
	glShadeModel(GL_SMOOTH);							// Enable Smooth Shading
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);				// Black Background
	glClearDepth(1.0f);									// Depth Buffer Setup
	glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
	glDepthFunc(GL_LEQUAL);								// The Type Of Depth Testing To Do
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculations
}

void spinCube() {
#ifndef MULTI_THREAD
	srand(time(NULL));
	for (int i = 0; i < totalcubes; i++) {
		spin[i] += rand() / 1000;
		if (spin[i] > 360.0)
			spin[i] = spin[i] - 360.0;
	}
#endif
	glutPostRedisplay();
}

void spinThread(PVOID param) {
	srand(time(NULL));
	for (int i = 0; i < totalcubes; i++) {
		spin[i] += rand() / 1000;
		if (spin[i] > 360.0)
			spin[i] = spin[i] - 360.0;
	}
}

void display(void) {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
										// Reset The Current Modelview Matrix
	
	int start = clock();
	int x, y;
#ifdef MULTI_THREAD
	_beginthread(spinThread, 0, NULL);
#endif
	for (int i = 0; i < totalcubes; i++){
		glLoadIdentity();
		x = i % 17;
		y = i / 17;
		glTranslatef(-24.0f + 3*x, 24.0f - 3*y, -60.0f);					// Move Left 1.5 Units And Into The Screen 6.0
		glRotatef(spin[i],0.0f,1.5f,-0.5f);
		glBegin(GL_QUADS);									// Start Drawing A Quad
			glColor3fv(GREEN);								// Set The Color To Green
			glVertex3f( 1.0f, 1.0f,-1.0f);					// Top Right Of The Quad (Top)
			glColor3fv(BLUE);
			glVertex3f(-1.0f, 1.0f,-1.0f);					// Top Left Of The Quad (Top)
			glColor3fv(RED);
			glVertex3f(-1.0f, 1.0f, 1.0f);					// Bottom Left Of The Quad (Top)
			glColor3fv(VIOLET);
			glVertex3f( 1.0f, 1.0f, 1.0f);					// Bottom Right Of The Quad (Top)
			glColor3fv(BLUE);								// Set The Color To Orange
			glVertex3f( 1.0f,-1.0f, 1.0f);					// Top Right Of The Quad (Bottom)
			glColor3fv(YELLOW);
			glVertex3f(-1.0f,-1.0f, 1.0f);					// Top Left Of The Quad (Bottom)
			glColor3fv(ORANGE);
			glVertex3f(-1.0f,-1.0f,-1.0f);					// Bottom Left Of The Quad (Bottom)
			glColor3fv(CYAN);
			glVertex3f( 1.0f,-1.0f,-1.0f);					// Bottom Right Of The Quad (Bottom)
			glColor3fv(VIOLET);
			glVertex3f( 1.0f, 1.0f, 1.0f);					// Top Right Of The Quad (Front)
			glColor3fv(RED);
			glVertex3f(-1.0f, 1.0f, 1.0f);					// Top Left Of The Quad (Front)
			glColor3fv(YELLOW);
			glVertex3f(-1.0f,-1.0f, 1.0f);					// Bottom Left Of The Quad (Front)
			glColor3fv(BLUE);
			glVertex3f( 1.0f,-1.0f, 1.0f);					// Bottom Right Of The Quad (Front)
			glColor3fv(CYAN);
			glVertex3f( 1.0f,-1.0f,-1.0f);					// Top Right Of The Quad (Back)
			glColor3fv(ORANGE);
			glVertex3f(-1.0f,-1.0f,-1.0f);					// Top Left Of The Quad (Back)
			glColor3fv(BLUE);
			glVertex3f(-1.0f, 1.0f,-1.0f);					// Bottom Left Of The Quad (Back)
			glColor3fv(GREEN);
			glVertex3f( 1.0f, 1.0f,-1.0f);					// Bottom Right Of The Quad (Back)
			glColor3fv(RED);
			glVertex3f(-1.0f, 1.0f, 1.0f);					// Top Right Of The Quad (Left)
			glColor3fv(BLUE);
			glVertex3f(-1.0f, 1.0f,-1.0f);					// Top Left Of The Quad (Left)
			glColor3fv(ORANGE);
			glVertex3f(-1.0f,-1.0f,-1.0f);					// Bottom Left Of The Quad (Left)
			glColor3fv(YELLOW);
			glVertex3f(-1.0f,-1.0f, 1.0f);					// Bottom Right Of The Quad (Left)
			glColor3fv(GREEN);						// Set The Color To Violet
			glVertex3f( 1.0f, 1.0f,-1.0f);					// Top Right Of The Quad (Right)
			glColor3fv(VIOLET);
			glVertex3f( 1.0f, 1.0f, 1.0f);					// Top Left Of The Quad (Right)
			glColor3fv(BLUE);
			glVertex3f( 1.0f,-1.0f, 1.0f);					// Bottom Left Of The Quad (Right)
			glColor3fv(CYAN);						// Set The Color To Yellow
			glVertex3f( 1.0f,-1.0f,-1.0f);					// Bottom Right Of The Quad (Right)
		glEnd();
		
	}
	int stop = clock();

	printf("Render Time: %f ms\n", float(stop - start)/1000);
	glutSwapBuffers();
}

void reshape(int width, int height) {
	if (height==0)										// Prevent A Divide By Zero By
	{
		height=1;										// Making Height Equal One
	}

	glViewport(0,0,width,height);						// Reset The Current Viewport

	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window
	gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glLoadIdentity();									// Reset The Modelview Matrix
}

int main(int argc, char** argv) { 
	
	nargs = argc;
	args = argv;
	glutInit(&nargs, args);
#if 0
	totalcubes = 30000;
	_beginthread(Render1, 0, NULL);
	_beginthread(Render2, 0, NULL);
	_beginthread(Render3, 0, NULL);
#else
	totalcubes = 1000;
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(640, 640);
	glutInitWindowPosition(800, 100);
	glutCreateWindow("Render");
	init();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutIdleFunc(spinCube);
	glutMainLoop();	
#endif
	
	while(1) ;
	

	return 0;
}   

void Render1(PVOID param) {
	
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(250, 250);
	glutInitWindowPosition(100, 100);
	glutCreateWindow("Render 1");
	init();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutIdleFunc(spinCube);
	glutMainLoop();	
}

void Render2(PVOID param) {
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(250, 250);
	glutInitWindowPosition(400, 100);
	glutCreateWindow("Render 2");
	init();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutIdleFunc(spinCube);
	glutMainLoop();
}

void Render3(PVOID param) {
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(250, 250);
	glutInitWindowPosition(700, 100);
	glutCreateWindow("Render 3");
	init();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutIdleFunc(spinCube);
	glutMainLoop();
}